using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

namespace Elte.SqlSpIdx.Indices
{
    public class VoronoiIndex : Index
    {
        private VoronoiDiagram diagram;
        private static Dictionary<string, VoronoiIndex> cache = new Dictionary<string, VoronoiIndex>();

        public VoronoiIndex(SqlTransaction tn)
            : base(tn)
        {
            type = IndexType.Voronoi;
        }

        public VoronoiIndex(SqlConnection cn)
            : base(cn)
        {
            type = IndexType.Voronoi;
        }

        /// <summary>
        /// Gets the number of cells (seed points)
        /// </summary>
        private int Count
        {
            get
            {
                return int.Parse(options["count"]);
            }
        }

        /// <summary>
        /// Gets if ridges are available
        /// </summary>
        public bool ComputeRidges
        {
            get
            {
                if (options.ContainsKey("ridges"))
                    return bool.Parse(options["ridges"]);
                else
                    return false;
            }
        }

        /// <summary>
        /// Gets if statistics are available
        /// </summary>
        public bool ComputeStatistics
        {
            get
            {
                if (options.ContainsKey("stat"))
                    return bool.Parse(options["stat"]);
                else
                    return false;
            }
        }

        /// <summary>
        /// Gets the populated VoronoiDiagram object that populates
        /// </summary>
        public VoronoiDiagram Diagram
        {
            get { return diagram; }
        }

        /// <summary>
        /// Loads the index and populates the topology object
        /// </summary>
        /// <param name="name"></param>
        public override void Load(string name)
        {
            base.Load(name);
            diagram = new VoronoiDiagram(this);
        }



        public override void CreateIndex(string name, string description, string table, string primaryKeyColumn, string indexColumn, string dataColumn, string dataColumnNames, string options)
        {
            bool newtransaction = false;
            if (databaseTransaction == null)
            {
                databaseTransaction = databaseConnection.BeginTransaction();
                newtransaction = true;
            }
            try
            {

                VoronoiIndex index = new VoronoiIndex(databaseTransaction);

                index.Name = name;
                index.Description = description;
                index.Table = table;
                index.PrimaryKeyColumn = primaryKeyColumn;
                index.DataColumn = dataColumn;
                index.DataColumnNames = dataColumnNames;
                index.Options = options;

                index.Save();

                //if (index.Whitening)
                //    index.CalculateWhiteningMatrix();


                System.Diagnostics.Debug.WriteLine("Generating tables...\r\n");

                index.CreateSchema();


                System.Diagnostics.Debug.WriteLine("Generating random seeds...\r\n");

                double[][] seeds = index.GenerateRandomCells();


                System.Diagnostics.Debug.WriteLine("Generating Voronoi tessalation...\r\n");

                index.diagram = new VoronoiDiagram(index);
                index.diagram.Generate(seeds);

                foreach (VoronoiCell cell in index.diagram.Cells)
                {
                    cell.CalculateBoundingBall();
                    cell.CalculateMinMax();
                    cell.CalculatePca();
                }



                index.diagram.Save();

            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Error: " + ex.Message + "\r\n");
            }
            finally
            {
                if (newtransaction) databaseTransaction.Commit();
            }
            System.Diagnostics.Debug.WriteLine("Done.\r\n");

        }




        //[Microsoft.SqlServer.Server.SqlFunction(DataAccess = DataAccessKind.Read)]
        //public static SqlInt32 LookupVoronoi(SqlString name, SqlBinary coord)
        //{
        //    VoronoiIndex index;

        //    if (!cache.ContainsKey(name.Value))
        //    {
        //        using (SqlConnection cn = new SqlConnection(Constants.MyConnectionString))
        //        {
        //            cn.Open();

        //            using (SqlTransaction tn = cn.BeginTransaction())
        //            {

        //                index = new VoronoiIndex(tn);
        //                index.Load(name.Value);

        //                //System.Diagnostics.Debug.WriteLine("Loading topology...\r\n");
        //                index.diagram.LoadCells();
        //                index.diagram.LoadHalfspaces();

        //                tn.Commit();
        //            }

        //            cn.Close();
        //        }

        //        cache.Add(name.Value, index);
        //    }
        //    else
        //    {
        //        index = cache[name.Value];
        //    }

        //    double[] v = Converters.VectorConverter.ToArray(coord.Value);
        //    // whitening
        //    v = VectorOperations.Subtract(v, index.mean);
        //    v = MatrixOperations.VectorProd(index.whiteningMatrix, v);

        //    return new SqlInt32(index.Lookup(v));

        //    /*
        //    System.Diagnostics.Debug.WriteLine("Performing lookup\r\n");

        //    //command to update rows
        //    sql = "UPDATE " + index.Table + " " +
        //          "SET SPIDX_" + index.Name + " = @Idx " +  //*** update to use indexcolumn!
        //          "WHERE " + index.PrimaryKeyColumn + " = @ID";
        //    SqlCommand updcmd = new SqlCommand(sql, cn);
        //    updcmd.Parameters.Add("@ID", SqlDbType.BigInt);
        //    updcmd.Parameters.Add("@Idx", SqlDbType.Int);

        //    // lookup is done in chuncks
        //    int chunksize = 10000, chunkcount;
        //    long[] ids = new long[chunksize];
        //    double[][] coords = new double[chunksize][];

        //    // table size
        //    sql = "SELECT COUNT(*) FROM " + index.Table;
        //    SqlCommand cmd = new SqlCommand(sql, cn);
        //    int count = Convert.ToInt32(cmd.ExecuteScalar());
        //    cmd.Dispose();

        //    //
        //    sql = "WITH OrderedTable AS " +
        //          "(SELECT ROW_NUMBER() OVER (ORDER BY " + index.PrimaryKeyColumn + ") rn," +
        //          index.PrimaryKeyColumn + ", " + index.DataColumn + " as coord FROM " + index.Table + ") " +
        //          "SELECT " + index.PrimaryKeyColumn + ", coord FROM OrderedTable WHERE rn BETWEEN @From AND @To";

        //    // debug code
        //    //count = 1;
        //    //sql = "WITH OrderedTable AS " +
        //    //      "(SELECT ROW_NUMBER() OVER (ORDER BY " + index.PrimaryKeyColumn + ") rn," +
        //    //      index.PrimaryKeyColumn + ", " + index.DataColumn + " as coord FROM " + index.Table + " WHERE ID = 587729232515301490) " +
        //    //      "SELECT " + index.PrimaryKeyColumn + ", coord FROM OrderedTable WHERE rn BETWEEN @From AND @To";

        //    //data reader to query rows
        //    cmd = new SqlCommand(sql, cn);
        //    cmd.Parameters.Add("@From", SqlDbType.Int);
        //    cmd.Parameters.Add("@To", SqlDbType.Int);

        //    //
        //    for (int i = 1; i <= count; i += chunksize)
        //    {
        //        cmd.Parameters["@From"].Value = i;
        //        cmd.Parameters["@To"].Value = i + chunksize - 1;

        //        SqlDataReader dr = cmd.ExecuteReader();
        //        chunkcount = 0;
        //        while (dr.Read())
        //        {
        //            ids[chunkcount] = dr.GetInt64(0);
        //            coords[chunkcount] = Converters.VectorConverter.ToArray(dr.GetSqlBinary(1).Value);

        //            chunkcount++;
        //        }
        //        dr.Close();
        //        dr.Dispose();

        //        updcmd.Transaction = cn.BeginTransaction();
        //        // performing update
        //        int jumps = 0, dists = 0;
        //        for (int q = 0; q < chunkcount; q++)
        //        {
        //            index.Lookup(updcmd, ids[q], coords[q], ref jumps, ref dists);
        //        }
        //        updcmd.Transaction.Commit();

        //        System.Diagnostics.Debug.WriteLine(String.Format("Done: {0} - {1}", i, i + chunksize - 1));
        //        System.Diagnostics.Debug.WriteLine(String.Format("Jumps: {0}, dists: {1}, count: {2}", jumps, dists, chunkcount));
        //    }

        //    //tn.Commit();
        //    //tn.Dispose();
        //    cn.Close();
        //    cn.Dispose();
        //     * 
        //     * */
        //}

        /*[Microsoft.SqlServer.Server.SqlProcedure]
        public static void LookupVoronoi(SqlString name)
        {
            string sql;

            SqlConnection cn = new SqlConnection(Constants.MyConnectionString);
            cn.Open();
            //SqlTransaction tn = cn.BeginTransaction();

            VoronoiIndex index = new VoronoiIndex(cn, null);
            index.Load(name.Value);

            System.Diagnostics.Debug.WriteLine("Loading topology...\r\n");
            index.diagram.LoadCells();
            index.diagram.LoadHyperplanes();

            System.Diagnostics.Debug.WriteLine("Performing lookup\r\n");

            //command to update rows
            sql = "UPDATE " + index.Table + " " +
                  "SET SPIDX_" + index.Name + " = @Idx " +  //*** update to use indexcolumn!
                  "WHERE " + index.PrimaryKeyColumn + " = @ID";
            SqlCommand updcmd = new SqlCommand(sql, cn);
            updcmd.Parameters.Add("@ID", SqlDbType.BigInt);
            updcmd.Parameters.Add("@Idx", SqlDbType.Int);
            
            // lookup is done in chuncks
            int chunksize = 10000, chunkcount;
            long[] ids = new long[chunksize];
            double[][] coords = new double[chunksize][];

            // table size
            sql = "SELECT COUNT(*) FROM " + index.Table;
            SqlCommand cmd = new SqlCommand(sql, cn);
            int count = Convert.ToInt32(cmd.ExecuteScalar());
            cmd.Dispose();

            //
            sql = "WITH OrderedTable AS " +
                  "(SELECT ROW_NUMBER() OVER (ORDER BY " + index.PrimaryKeyColumn + ") rn," +
                  index.PrimaryKeyColumn + ", " + index.DataColumn + " as coord FROM " + index.Table + ") " +
                  "SELECT " + index.PrimaryKeyColumn + ", coord FROM OrderedTable WHERE rn BETWEEN @From AND @To";

            // debug code
            //count = 1;
            //sql = "WITH OrderedTable AS " +
            //      "(SELECT ROW_NUMBER() OVER (ORDER BY " + index.PrimaryKeyColumn + ") rn," +
            //      index.PrimaryKeyColumn + ", " + index.DataColumn + " as coord FROM " + index.Table + " WHERE ID = 587729232515301490) " +
            //      "SELECT " + index.PrimaryKeyColumn + ", coord FROM OrderedTable WHERE rn BETWEEN @From AND @To";

            //data reader to query rows
            cmd = new SqlCommand(sql, cn);
            cmd.Parameters.Add("@From", SqlDbType.Int);
            cmd.Parameters.Add("@To", SqlDbType.Int);

            //
            for (int i = 1; i <= count; i += chunksize)
            {
                cmd.Parameters["@From"].Value = i;
                cmd.Parameters["@To"].Value = i + chunksize - 1;

                SqlDataReader dr = cmd.ExecuteReader();
                chunkcount = 0;
                while (dr.Read())
                {
                    ids[chunkcount] = dr.GetInt64(0);
                    coords[chunkcount] = Converters.VectorConverter.ToArray(dr.GetSqlBinary(1).Value);

                    chunkcount++;
                }
                dr.Close();
                dr.Dispose();

                updcmd.Transaction = cn.BeginTransaction();
                // performing update
                int jumps = 0, dists = 0;
                for (int q = 0; q < chunkcount; q++)
                {
                    index.Lookup(updcmd, ids[q], coords[q], ref jumps, ref dists);
                }
                updcmd.Transaction.Commit();

                System.Diagnostics.Debug.WriteLine(String.Format("Done: {0} - {1}", i, i + chunksize - 1));
                System.Diagnostics.Debug.WriteLine(String.Format("Jumps: {0}, dists: {1}, count: {2}", jumps, dists, chunkcount));
            }

            //tn.Commit();
            //tn.Dispose();
            cn.Close();
            cn.Dispose();
        }*/

        protected override void CreateSchema()
        {
            CreateTable(Voronoi.VoronoiScripts.CellsTableSchema, "Cells", "", false);
            CreateTable(Voronoi.VoronoiScripts.HalfspacesTableSchema, "Hspaces", "", false);
            CreateTable(Voronoi.VoronoiScripts.VerticesTableSchema, "Vertices", "", false);
            CreateTable(Voronoi.VoronoiScripts.VSRelTableSchema, "VSRel", "", false);
            if (ComputeRidges) CreateTable(Voronoi.VoronoiScripts.RidgesTableSchema, "Ridges", "", false);
        }

        public override void RenameSchema()
        {

        }

        protected override void DeleteSchema()
        {
            DropTable("VSRel");
            DropTable("Vertices");
            DropTable("Hspaces");
            DropTable("Cells");
            if (ComputeRidges) DropTable("Ridges");
        }

        public double[][] GenerateRandomCells()
        {
            // Calculating number of data points in order to calibrate random seed generator
            string sql = "SELECT COUNT(*) FROM " + Table;
            SqlCommand cmd = new SqlCommand(sql, databaseTransaction.Connection, databaseTransaction);
            int datacount = Convert.ToInt32(cmd.ExecuteScalar());

            // Generating random cells
            //sql = "WITH OrderedObjects AS " +
            //             "(SELECT " + DataColumn + " as Coord, ROW_NUMBER() OVER (ORDER BY " + PrimaryKeyColumn + ") rn " +
            //             "FROM " + Table + ") " +
            //             "INSERT " + Table + Constants.SPIDX + Name + "_Cells (CellID, Coord) " +
            //             "SELECT (ROW_NUMBER() OVER (ORDER BY Coord)) - 1, dbo.Joggle(Coord, 0.01) FROM OrderedObjects " +
            //             "INNER JOIN (SELECT * FROM dbo.RandomList(" + datacount.ToString() + ", " + Count.ToString() + ")) p ON p.randomvalue = rn";

            sql = "WITH OrderedObjects AS " +
                         "(SELECT " + DataColumn + " as Coord, ROW_NUMBER() OVER (ORDER BY " + PrimaryKeyColumn + ") rn " +
                         "FROM " + Table + ") " +
                         "SELECT dbo.VectorJoggle(Coord, 0.01) FROM OrderedObjects " +
                         "INNER JOIN (SELECT * FROM dbo.RandomList(" + datacount.ToString() + ", " + Count.ToString() + ")) p ON p.randomvalue = rn";
            cmd = new SqlCommand(sql, databaseTransaction.Connection, databaseTransaction);
            SqlDataReader dr = cmd.ExecuteReader();

            double[][] seeds = new double[Count][];

            byte[] buffer = new byte[Dimensions * sizeof(double) + sizeof(int) + sizeof(int)];
            int q = 0;
            while (dr.Read())
            {
                dr.GetBytes(0, 0, buffer, 0, buffer.Length);
                seeds[q] = Converters.VectorConverter.ToArray(buffer);
                q++;
            }

            cmd.Dispose();
            dr.Close();
            dr.Dispose();

            // whitening
            if (Whitening)
            {
                for (int i = 0; i < seeds.Length; i++)
                {
                    seeds[i] = MatrixOperations.VectorProd(base.whiteningMatrix, VectorOperations.Subtract(seeds[i], base.mean));
                }
            }

            // sorting array by coordinates
            Array.Sort<double[]>(seeds, new Utils.LexicalComparer());

            // ***  Move to diagram save!
            sql = "INSERT " + Table + Constants.SPIDX + Name + "_Cells (CellID, Coord) VALUES (@CellID, @Coord)";
            cmd = new SqlCommand(sql, databaseTransaction.Connection, databaseTransaction);

            cmd.Parameters.Add("@CellID", SqlDbType.Int);
            cmd.Parameters.Add("@Coord", SqlDbType.VarBinary);

            for (int i = 0; i < seeds.Length; i++)
            {
                cmd.Parameters["@CellID"].Value = i;
                cmd.Parameters["@Coord"].Value = Converters.VectorConverter.ToBinary(seeds[i]);

                cmd.ExecuteNonQuery();
            }

            cmd.Dispose();

            return seeds;
        }



        //[Microsoft.SqlServer.Server.SqlProcedure]
        //public static void VoronoiSearch(SqlString indexName, SqlString rowSource, SqlString columnNames, SqlString constraints)
        //{
        //    SqlConnection cn = new SqlConnection(Constants.MyConnectionString);
        //    cn.Open();
        //    SqlTransaction tn = cn.BeginTransaction();

        //    VoronoiIndex index = new VoronoiIndex(tn);
        //    index.Load(indexName.Value);

        //    DateTime t = DateTime.Now;
        //    System.Diagnostics.Debug.WriteLine("Loading cells...");
        //    index.diagram.LoadCells();
        //    System.Diagnostics.Debug.WriteLine(" loaded in " + (DateTime.Now - t).Milliseconds + " msec");

        //    t = DateTime.Now;
        //    System.Diagnostics.Debug.WriteLine("Loading adjacencies...");
        //    index.diagram.LoadHalfspaces();
        //    System.Diagnostics.Debug.WriteLine(" loaded in " + (DateTime.Now - t).Milliseconds + " msec");

        //    t = DateTime.Now;
        //    System.Diagnostics.Debug.WriteLine("Loading vertices...");
        //    index.diagram.LoadVertices();
        //    System.Diagnostics.Debug.WriteLine(" loaded in " + (DateTime.Now - t).Milliseconds + " msec");

        //    Parser.Parser p = new Parser.Parser(new Parser.Tokenizer(), new Parser.VariableMapper(columnNames.Value));
        //    HalfspaceList ls = p.Parse(constraints.Value, index.Dimensions);

        //    t = DateTime.Now;
        //    System.Diagnostics.Debug.WriteLine("Checking section...");
        //    index.Diagram.CheckCellSections(ls, false, true);
        //    System.Diagnostics.Debug.WriteLine(" checked in " + (DateTime.Now - t).Milliseconds + " msec");

        //    string sql = " SELECT " + index.Table + "." + index.PrimaryKeyColumn +
        //                 " FROM " + index.Table +
        //                 " INNER JOIN " + index.Table + Constants.SPIDX + index.Name + " i " +
        //                 " ON " + index.Table + "." + index.PrimaryKeyColumn + " = i.ID " +
        //                 " WHERE i.CellID = @CellID";

        //    SqlCommand cmd = new SqlCommand(sql, cn, tn);
        //    SqlParameter id = cmd.Parameters.Add("@CellID", SqlDbType.Int);
        //    SqlDataReader dr;
        //    if (Microsoft.SqlServer.Server.SqlContext.IsAvailable)
        //    {
        //        SqlDataRecord rec = new SqlDataRecord(
        //            new SqlMetaData(index.PrimaryKeyColumn, SqlDbType.BigInt));

        //        SqlContext.Pipe.SendResultsStart(rec);

        //        foreach (VoronoiCell cell in index.Diagram.Cells)
        //        {
        //            id.Value = cell.Id;
        //            dr = cmd.ExecuteReader();

        //            switch (cell.SectionType)
        //            {
        //                case SectionType.Containing:
        //                    {
        //                        while (dr.Read())
        //                        {
        //                            rec.SetInt64(0, dr.GetInt64(0));
        //                            SqlContext.Pipe.SendResultsRow(rec);
        //                        }
        //                        break;
        //                    }
        //                case SectionType.Intersecting:
        //                case SectionType.Problematic:
        //                    {
        //                        break;
        //                    }
        //            }

        //            dr.Close();
        //            dr.Dispose();
        //        }

        //        SqlContext.Pipe.SendResultsEnd();
        //    }
        //    tn.Commit();
        //    tn.Dispose();
        //    cn.Close();
        //    cn.Dispose();
        //}

        //[Microsoft.SqlServer.Server.SqlFunction(
        //    DataAccess = DataAccessKind.Read, FillRowMethodName = "VoronoiCellSearchFill",
        //    IsDeterministic = true, IsPrecise = true,
        //    TableDefinition = "CellID int, Section tinyint")]
        //public static IEnumerable VoronoiCellSearch(SqlString indexName, SqlString columnNames, SqlString constraints)
        //{
        //    VoronoiIndex index;

        //    if (!cache.ContainsKey(indexName.Value))
        //    {
        //        SqlConnection cn = new SqlConnection(Constants.MyConnectionString);
        //        cn.Open();
        //        SqlTransaction tn = null; //SqlTransaction tn = cn.BeginTransaction();

        //        index = new VoronoiIndex(cn);
        //        index.Load(indexName.Value);

        //        index.Diagram.LoadCells();
        //        index.Diagram.LoadHalfspaces();
        //        index.Diagram.LoadVertices();
        //        index.Diagram.LoadVSRelations();

        //        cn.Close();
        //        cn.Dispose();

        //        cache.Add(indexName.Value, index);
        //    }
        //    else
        //    {
        //        index = cache[indexName.Value];
        //    }

        //    Parser.Parser p = new Parser.Parser(new Parser.Tokenizer(), new Parser.VariableMapper(columnNames.Value));
        //    HalfspaceList ls = p.Parse(constraints.Value, index.Dimensions);

        //    // converting inequalities to the basis of the voroni cells
        //    // recentering

        //    if (index.Whitening)
        //    {
        //        //****
        //        Mapack.Matrix M = new Mapack.Matrix(index.whiteningMatrix.Length, index.whiteningMatrix[0].Length);
        //        for (int i = 0; i < index.whiteningMatrix.Length; i++)
        //            for (int j = 0; j < index.whiteningMatrix[i].Length; j++)
        //                M[i, j] = index.whiteningMatrix[i][j];
        //        Mapack.Matrix MinvT2 = M.Inverse.Transpose();
        //        double[][] MinvT = new double[MinvT2.Rows][];
        //        for (int i = 0; i < index.whiteningMatrix.Length; i++)
        //        {
        //            MinvT[i] = new double[MinvT2.Columns];
        //            for (int j = 0; j < index.whiteningMatrix[i].Length; j++)
        //                MinvT[i][j] = (double)MinvT2[i, j];
        //        }

        //        foreach (Halfspace l in ls)
        //        {
        //            // centering
        //            double d2 = VectorOperations.DotProd(l.A, index.mean);
        //            l.B -= d2;

        //            // x0
        //            double[] x0 = new double[l.A.Length];
        //            for (int i = 0; i < l.A.Length; i++)
        //                x0[i] = l.B * l.A[i];

        //            // transforming the unit vector
        //            l.A = MatrixOperations.VectorProd(MinvT, l.A);
        //            double norm = (double)Math.Sqrt(VectorOperations.DotProd(l.A, l.A));
        //            for (int i = 0; i < l.A.Length; i++)
        //                l.A[i] /= norm;

        //            // tranforming the inhomogeneity
        //            l.B = VectorOperations.DotProd(l.A, MatrixOperations.VectorProd(index.whiteningMatrix, x0));
        //        }
        //    }

        //    index.Diagram.CheckCellSections(ls, true, false);

        //    //foreach (VoronoiCell c in index.Diagram.Cells)
        //    //    if (c.SectionType == SectionType.Containing ||
        //    //        c.SectionType == SectionType.Intersecting || c.SectionType == SectionType.Problematic)
        //    //        yield return c;

        //    return index.Diagram.Cells;
        //}

        public static void VoronoiCellSearchFill(object obj, out SqlInt32 cellId, out SqlByte section)
        {
            VoronoiCell c = (VoronoiCell)obj;
            cellId = new SqlInt32(c.Id);
            section = new SqlByte((byte)c.SectionType);
        }

        private static VoronoiIndex GetIndex(SqlString name)
        {
            VoronoiIndex index;

            if (!cache.ContainsKey(name.Value))
            {
                using (SqlConnection cn = new SqlConnection(Constants.MyConnectionString))
                {
                    cn.Open();

                    index = new VoronoiIndex(cn);
                    index.Load(name.Value);

                    index.DatabaseConnection = cn;
                    cn.Close();
                }

                cache.Add(name.Value, index);
            }
            else
            {
                index = cache[name.Value];
            }

            return index;
        }


        #region "Index functions"



        public override int LookupIndex(double[] coord)
        {

            // for bookkeeping of visited cells
            bool[] seen = new bool[diagram.Cells.Count];
            //Array.Clear(seen, 0, seen.Length);

            // lookup closest seed, always start from first
            bool foundcloser = false;
            VoronoiCell closer = diagram.Cells[0];
            double closerdist = VectorOperations.Dist2(closer.Coord, coord);
            VoronoiCell cell = diagram.Cells[0];
            seen[0] = true;


            while (true)
            {
                foundcloser = false;
                double dist = closerdist;
                //dists++;

                // measuring distance from all neighbours
                foreach (VoronoiCell neighbor in cell.Neighbors)
                {
                    if (seen[neighbor.Id]) continue;

                    double d = VectorOperations.Dist2(neighbor.Coord, coord);
                    //dists++;
                    seen[neighbor.Id] = true;
                    if (d < dist)
                    {
                        dist = closerdist = d;
                        foundcloser = true;
                        closer = neighbor;
                    }
                }

                if (!foundcloser)
                {
                    // closest cell found
                    return cell.Id;
                }
                else
                {
                    // step to the closest and repeat
                    cell = closer;
                    //jumps++;
                }

            }

            //return;

        }

        public override IEnumerable NearestNeighborsIndex(double[] center, int N)
        {
            throw new Exception("The method or operation is not implemented.");
        }


        public override IEnumerable SpherePoints(double[] coord, double R)
        {
            throw new Exception("The method or operation is not implemented.");
        }
        #endregion





        public override IEnumerable<object[]> SpatialQuery(string selectlist, string spatialwhere, string sqlwhere)
        {
            throw new NotImplementedException();
        }

        
    }
}
